/*
 * Copyright 2005-2013 rsico. All rights reserved.
 * Support: http://www.rsico.cn
 * License: http://www.rsico.cn/license
 */
package com.rzico.core.plugin.weixin;

import java.io.*;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.text.DecimalFormat;
import java.util.*;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;

import com.rzico.core.entity.SysPlugin;
import com.rzico.core.entity.SysUser;
import com.rzico.core.plugin.PaymentPlugin;
import com.rzico.util.IPUtils;
import com.rzico.util.MD5Utils;
import com.rzico.util.StringUtils;
import com.rzico.util.XMLUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Plugin - 微信支付(普通商户/特约商户)
 * @author rsico Team
 * @version 3.0
 */
@Component("weixinPayPlugin")
@Slf4j
public class WeiXinPayPlugin extends PaymentPlugin {

	public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	public static final String REFUNDS_ORDER_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

	@Value("${weixin.appid}")
	private String wx_appid;
	@Value("${weixin.mchid}")
	private String wx_mchid;
	@Value("${weixin.mchSecret}")
	private String wx_mch_secret;
	@Value("${setting.host}")
	private String host;

	@Override
	public String getName() {
		return "微信支付(公众号)";
	}

	@Override
	public String getVersion() {
		return "1.0";
	}

	/**
	 * 作用：生成签名
	 */
	public String getSign(HashMap<String, Object> params, SysPlugin sysPlugin) throws Exception {
		String str = joinKeyValue(params,"&",true,"sign","key");
		// 签名步骤二：在string后加入KEY
		if ("1".equals(sysPlugin.getAttribute("method"))) {
			str += "&key=" + sysPlugin.getAttribute("key");
		} else {
			str += "&key=" + wx_mch_secret;
		}
		// 签名步骤三：MD5加密 签名步骤四：所有字符转为大写
		str = MD5Utils.getMD5Str(str).toUpperCase();
		return str;
	}

	/**
	 * 获取用户号
	 * @param sysUser SysUser
	 * @return 获取用户号
	 */
	@Override
	public String getUserId(SysUser sysUser) {
		return sysUser.getWxId();
	}

	@Override
	public Map<String, Object> trade(SysPlugin sysPlugin,String tradeNo, String description, BigDecimal totalfee, String userId, HttpServletRequest request) {

		HashMap<String, Object> packageParams = new HashMap<>();
		HashMap<String, Object> finalpackage = new HashMap<>();
		String createNoncestr = StringUtils.CreateNoncestr();

		if ("1".equals(sysPlugin.getAttribute("method"))) {
			packageParams.put("appid", sysPlugin.getAttribute("appid"));
			packageParams.put("mch_id", sysPlugin.getAttribute("mchid"));
		} else {
			packageParams.put("appid", wx_appid);
			packageParams.put("mch_id", wx_mchid);
			packageParams.put("sub_appid", sysPlugin.getAttribute("appid"));
			packageParams.put("sub_mch_id", sysPlugin.getAttribute("mchid"));
		}

		packageParams.put("nonce_str", createNoncestr);
		description = description.trim().replaceAll("[^\u4E00-\u9FA5]", "");
		if (description.length()>40) {
			packageParams.put("body", description.substring(0, 40));
		} else {
			packageParams.put("body", description);
		}
		packageParams.put("out_trade_no", tradeNo);

		// 这里写的金额为1 分到时修改
		packageParams.put("total_fee", totalfee.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).toString());
		packageParams.put("spbill_create_ip", IPUtils.getIp(request));
		packageParams.put("notify_url", getNotifyUrl(sysPlugin));

		if (userId==null) {
			packageParams.put("trade_type", "MWEB");
		} else {
			packageParams.put("trade_type", "JSAPI");
			if ("1".equals(sysPlugin.getAttribute("method"))) {
				packageParams.put("openid", userId);
			} else {
				packageParams.put("sub_openid", userId);
			}
		}
		packageParams.put("attach",sysPlugin.getMchId());

		try {
			String sign = getSign(packageParams,sysPlugin);
			packageParams.put("sign", sign);


		} catch (Exception e) {
			finalpackage.put("return_code", "9999");
			finalpackage.put("error_msg", "设置异常");
			return finalpackage;
		}

		try {
			String resultXml = XMLUtils.mapToXml(packageParams);

			String respStr = post(UNIFIED_ORDER_URL,resultXml,null);

			Map resultMap = XMLUtils.xmlToMap(respStr);
			String return_code = (String) resultMap.get("return_code");
			if (return_code.equals("SUCCESS")) {
				if (resultMap.containsKey("mweb_url")) {
					finalpackage.put("mweb_url", resultMap.get("mweb_url"));
				} else {
					String packages = "prepay_id=" + (String) resultMap.get("prepay_id");
					finalpackage.put("appId", sysPlugin.getAttribute("appid"));
					finalpackage.put("package", packages);
					finalpackage.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
					finalpackage.put("nonceStr", StringUtils.CreateNoncestr());
					finalpackage.put("signType", "MD5");
					String finalsign = getSign(finalpackage, sysPlugin);
					finalpackage.put("paySign", finalsign);
				}
				finalpackage.put("return_code", "0000");
			} else {
				finalpackage.put("return_code", "0003");
				finalpackage.put("error_msg", (String) resultMap.get("return_msg"));

			}

		} catch (Exception e) {
			finalpackage.put("return_code", "9999");
			finalpackage.put("error_msg", "设置异常");
		}
		log.info("收款:"+finalpackage);
		return finalpackage;

	}

	@Override
	public Map<String, Object> verifyNotify(SysPlugin sysPlugin,HttpServletRequest request) {
		HashMap<String, Object> finalpackage = new HashMap<>();
		try {
			HashMap<String, Object> resultMap = new HashMap<String, Object>();
			StringBuffer info = new StringBuffer();
			InputStream in = request.getInputStream();
			BufferedInputStream buf = new BufferedInputStream(in);
			byte[] buffer = new byte[1024];
			int iRead;
			while ((iRead = buf.read(buffer)) != -1) {
				info.append(new String(buffer, 0, iRead, "UTF-8"));
			}
			resultMap = XMLUtils.xmlToMap(info.toString());
			log.info("异常通知:"+info.toString());
			if (resultMap.get("result_code").toString().equals("SUCCESS")) {
				String sign = getSign(resultMap,sysPlugin);
				if (sign.equals(resultMap.get("sign").toString())) {
					finalpackage.put("return_code", "0000");
					finalpackage.put("tradeNo", (String) resultMap.get("out_trade_no"));
					finalpackage.put("tranSn", (String) resultMap.get("transaction_id"));
					return finalpackage;
				}
			}
			finalpackage.put("return_code", "9999");
			finalpackage.put("error_msg",(String) resultMap.get("return_msg"));
		} catch (Exception e) {
			finalpackage.put("return_code", "9999");
			finalpackage.put("error_msg", e.getMessage());
		}
		return finalpackage;
	}

	/**
	 * 查询订单的支付结果  0000成功  9999处理中  其他的失败 
	 */
	@Override
    public Map<String, Object> tradeQuery(SysPlugin sysPlugin,String tradeNo,String tranSn,HttpServletRequest request) {

		HashMap<String, Object> finalpackage = new HashMap<>();
		String createNoncestr = StringUtils.CreateNoncestr();
		HashMap<String, Object> parameterMap = new HashMap<String, Object>();
		if ("1".equals(sysPlugin.getAttribute("method"))) {
			parameterMap.put("appid", sysPlugin.getAttribute("appid"));
			parameterMap.put("mch_id", sysPlugin.getAttribute("mchid"));
		} else {
			parameterMap.put("appid", wx_appid);
			parameterMap.put("mch_id", wx_mchid);
			parameterMap.put("sub_appid", sysPlugin.getAttribute("appid"));
			parameterMap.put("sub_mch_id", sysPlugin.getAttribute("mchid"));
		}
		parameterMap.put("out_trade_no",tradeNo);
		parameterMap.put("nonce_str", createNoncestr);
		try {
			parameterMap.put("sign",getSign(parameterMap,sysPlugin));
		} catch (Exception e) {
			finalpackage.put("return_code", "0003");
			finalpackage.put("error_msg","签名出错");
			return finalpackage;
		}

		try {
			String xml = XMLUtils.mapToXml(parameterMap);
			String resultXml = post("https://api.mch.weixin.qq.com/pay/orderquery",xml,null);
			Map<String,Object> resultMap = XMLUtils.xmlToMap(resultXml);
			String return_code = (String) resultMap.get("return_code");
			if (return_code.equals("SUCCESS")) {
				String result = (String) resultMap.get("result_code");
				if ("SUCCESS".equals(result)) {
					String status = (String) resultMap.get("trade_state");
					if (status.equals("SUCCESS")) {
						finalpackage.put("tranSn", (String) resultMap.get("transaction_id"));
						finalpackage.put("return_code", "0000");
						finalpackage.put("error_msg","支付成功");
					} else if (status.equals("REFUND")) {
						finalpackage.put("return_code", "0002");
						finalpackage.put("error_msg","转入退款");
					} else if (status.equals("REVOKED")) {
						finalpackage.put("return_code", "0002");
						finalpackage.put("error_msg","已撤消");
					} else if (status.equals("USERPAYING")) {
						finalpackage.put("return_code", "0001");
						finalpackage.put("error_msg","正在付款");
					} else if (status.equals("PAYERROR")) {
						finalpackage.put("return_code", "0003");
						finalpackage.put("error_msg","未知错误");
					} else if (status.equals("NOTPAY")) {
						finalpackage.put("return_code", "0003");
						finalpackage.put("error_msg","订单未支付");
					} else {
						finalpackage.put("return_code", "0003");
						finalpackage.put("error_msg", (String) resultMap.get("trade_state_desc"));
					}
				} else  {
					String errCode = (String) resultMap.get("err_code");
					if ("ORDERNOTEXIST".equals(errCode)) {
						finalpackage.put("return_code", "0003");
						finalpackage.put("error_msg", (String) resultMap.get("err_code_des"));
					} else {
						finalpackage.put("return_code", "9999");
						finalpackage.put("error_msg", (String) resultMap.get("err_code_des"));
					}
				}
			} else
			{
				finalpackage.put("return_code", "9999");
				finalpackage.put("error_msg", (String) resultMap.get("return_msg"));
			}
		} catch (Exception e) {
			finalpackage.put("return_code", "9999");
			finalpackage.put("error_msg", "设置异常");
		}

		return finalpackage;
	}
	
	@Override
	public String getNotifyMessage(HttpServletRequest request) {
		return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
	}

	/**
	 * 申请退款
	 */
	@Override
	public Map<String, Object> refund(SysPlugin sysPlugin, java.lang.String refundNo, java.lang.String tradeNo, BigDecimal totalfee, BigDecimal refundfee , HttpServletRequest request) {
		HashMap<String, Object> finalpackage = new HashMap<String, Object>();
		HashMap<String, Object> paramsMap = new HashMap<String, Object>();
		DecimalFormat decimalFormat = new DecimalFormat("#");
		if ("1".equals(sysPlugin.getAttribute("method"))) {
			paramsMap.put("appid", sysPlugin.getAttribute("appid"));
			paramsMap.put("mch_id", sysPlugin.getAttribute("mchid"));
		} else {
			paramsMap.put("appid", wx_appid);
			paramsMap.put("mch_id", wx_mchid);
			paramsMap.put("sub_appid", sysPlugin.getAttribute("appid"));
			paramsMap.put("sub_mch_id", sysPlugin.getAttribute("mchid"));
		}
		paramsMap.put("nonce_str", String.valueOf(new Date().getTime()));
		paramsMap.put("out_trade_no",tradeNo);
		paramsMap.put("out_refund_no",refundNo);
		paramsMap.put("total_fee", decimalFormat.format(totalfee.multiply(new BigDecimal(100))));
		paramsMap.put("refund_fee", decimalFormat.format(refundfee.multiply(new BigDecimal(100))));
		try {
			paramsMap.put("sign",getSign(paramsMap,sysPlugin));
		} catch (Exception e) {
			finalpackage.put("return_code", "0003");
			finalpackage.put("error_msg","签名出错");
			return finalpackage;
		}

		String reqUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

		try {
			String xml = XMLUtils.mapToXml(paramsMap);
			HttpClient httpClient =	createSSLHttpClient(sysPlugin);
			String resultXml = post(reqUrl,xml,httpClient);
			HashMap<String,Object> resultMap = XMLUtils.xmlToMap(resultXml);
			if(resultMap.containsKey("sign")){
				String sign = getSign(resultMap,sysPlugin);
				if(!sign.equals(resultMap.get("sign"))){
					finalpackage.put("return_code", "0003");
					finalpackage.put("error_msg", "验证签名不通过");
					return finalpackage;
				}else{
					if("SUCCESS".equals(resultMap.get("return_code").toString()) && "SUCCESS".equals(resultMap.get("result_code").toString())){
						finalpackage.put("return_code", "0000");
						finalpackage.put("error_msg", "提交成功");
						finalpackage.put("tranSn", (String) resultMap.get("transaction_id"));
						return finalpackage;
					}else{
						finalpackage.put("return_code", "0003");
						finalpackage.put("error_msg", resultMap.get("err_code_des").toString());
						return finalpackage;
					}
				}
			}else{

				finalpackage.put("return_code", "0003");
				finalpackage.put("error_msg", resultMap.get("return_msg").toString());
				return finalpackage;
			}
		} catch (Exception e) {
			finalpackage.put("return_code", "0003");
			finalpackage.put("error_msg","提交银行出错");
			return finalpackage;
		}


	}

	/**
	 * 查询退款
	 */
	@Override
	public Map<String, Object> refundQuery(SysPlugin sysPlugin, String refundNo,String tranSn, HttpServletRequest request) {
		HashMap<String, Object> paramsMap = new HashMap<String, Object>();
		HashMap<String, Object> finalpackage = new HashMap<String, Object>();
		if ("1".equals(sysPlugin.getAttribute("method"))) {
			paramsMap.put("appid", sysPlugin.getAttribute("appid"));
			paramsMap.put("mch_id", sysPlugin.getAttribute("mchid"));
		} else {
			paramsMap.put("appid", wx_appid);
			paramsMap.put("mch_id", wx_mchid);
			paramsMap.put("sub_appid", sysPlugin.getAttribute("appid"));
			paramsMap.put("sub_mch_id", sysPlugin.getAttribute("mchid"));
		}
		paramsMap.put("out_refund_no",refundNo);
		paramsMap.put("nonce_str", String.valueOf(new Date().getTime()));
		try {
			paramsMap.put("sign",getSign(paramsMap,sysPlugin));
		} catch (Exception e) {
			finalpackage.put("return_code", "9999");
			finalpackage.put("error_msg","签名出错");
			return finalpackage;
		}

		String reqUrl = "https://api.mch.weixin.qq.com/pay/refundquery";

			try {
				String xml = XMLUtils.mapToXml(paramsMap);
				HttpClient httpClient =	createSSLHttpClient(sysPlugin);
				String resultXml = post(reqUrl,xml,httpClient);
				HashMap<String,Object> resultMap = XMLUtils.xmlToMap(resultXml);
				if (resultMap.containsKey("sign")) {
					String sign = getSign(resultMap,sysPlugin);
					if(!sign.equals(resultMap.get("sign").toString())){
						finalpackage.put("return_code", "9999");
						finalpackage.put("result_msg","签名出错");
						return finalpackage;
					} else {
						if ("SUCCESS".equals(resultMap.get("return_code").toString()) && "SUCCESS".equals(resultMap.get("result_code").toString())) {
							int count = Integer.parseInt(resultMap.get("refund_count").toString())-1;
							if ("SUCCESS".equals(resultMap.get("refund_status_"+count).toString())) {
								finalpackage.put("return_code", "0000");
								finalpackage.put("error_msg","退款成功");
								finalpackage.put("tranSn", (String) resultMap.get("transaction_id"));
								return finalpackage;
							} else if ("PROCESSING".equals(resultMap.get("refund_status_"+count).toString())) {
								finalpackage.put("return_code", "0001");
								finalpackage.put("error_msg","退款处理中");
								return finalpackage;
							} else if ("REFUNDCLOSE".equals(resultMap.get("refund_status_"+count).toString())) {
								finalpackage.put("return_code", "0003");
								finalpackage.put("error_msg","退款失败");
								return finalpackage;
							} else if("CHANGE".equals(resultMap.get("refund_status_"+count).toString())){
								finalpackage.put("return_code", "0003");
								finalpackage.put("error_msg","退款失败");
								return finalpackage;
							} else {
								finalpackage.put("return_code", "9999");
								finalpackage.put("error_msg",resultMap.get("return_msg").toString());
								return finalpackage;
							}
						} else {
							finalpackage.put("return_code", "9999");
							finalpackage.put("error_msg",resultMap.get("err_code_des").toString());
							return finalpackage;
						}
					}
				} else {
					finalpackage.put("return_code", "9999");
					finalpackage.put("error_msg",resultMap.get("err_code_des").toString());
					return finalpackage;
				}

			} catch (Exception e) {
				finalpackage.put("return_code", "9999");
				finalpackage.put("error_msg",e.getMessage());
				return finalpackage;
			}

	}



}